using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.Elasticsearch;
using hao_Common.Logging.Options;
using hao_Common.Logging.Middleware;

namespace hao_Common.Logging.Extensions
{
    /// <summary>
    /// 日志扩展方法
    /// </summary>
    public static class LoggingExtensions
    {
        /// <summary>
        /// 添加Serilog日志
        /// </summary>
        /// <param name="builder">主机构建器</param>
        /// <param name="configuration">配置</param>
        /// <returns></returns>
        public static IHostBuilder AddSerilog(this IHostBuilder builder, IConfiguration? configuration = null)
        {
            return builder.UseSerilog((context, services, loggerConfig) =>
            {
                var config = configuration ?? context.Configuration;
                ConfigureSerilog(loggerConfig, config, context.HostingEnvironment.EnvironmentName);
            });
        }

        /// <summary>
        /// 添加日志服务
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <param name="configuration">配置</param>
        /// <returns></returns>
        public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration configuration)
        {
            // 绑定日志配置
            services.Configure<LoggingOptions>(configuration.GetSection(LoggingOptions.SectionName));
            
            // 添加日志中间件
            services.AddScoped<RequestLoggingMiddleware>();
            
            return services;
        }

        /// <summary>
        /// 配置Serilog
        /// </summary>
        /// <param name="loggerConfig">日志配置</param>
        /// <param name="configuration">应用配置</param>
        /// <param name="environmentName">环境名称</param>
        private static void ConfigureSerilog(LoggerConfiguration loggerConfig, IConfiguration configuration, string environmentName)
        {
            var loggingOptions = new LoggingOptions();
            configuration.GetSection(LoggingOptions.SectionName).Bind(loggingOptions);

            // 基础配置
            loggerConfig
                .MinimumLevel.Is(ParseLogLevel(loggingOptions.LogLevel))
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .Enrich.WithProperty("Application", loggingOptions.ApplicationName)
                .Enrich.WithProperty("Environment", environmentName)
                .Enrich.WithMachineName()
                .Enrich.WithProcessId()
                .Enrich.WithThreadId();

            // 控制台日志
            if (loggingOptions.EnableConsole)
            {
                loggerConfig.WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}");
            }

            // 文件日志
            if (loggingOptions.EnableFile)
            {
                loggerConfig.WriteTo.File(
                    path: loggingOptions.File.Path,
                    rollingInterval: RollingInterval.Day,
                    fileSizeLimitBytes: loggingOptions.File.FileSizeLimitMB * 1024 * 1024,
                    retainedFileCountLimit: loggingOptions.File.RetainedFileCountLimit,
                    rollOnFileSizeLimit: loggingOptions.File.RollOnFileSizeLimit,
                    outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}");
            }

            // Elasticsearch日志
            if (loggingOptions.EnableElasticsearch)
            {
                var elasticOptions = new ElasticsearchSinkOptions(new Uri(loggingOptions.Elasticsearch.Uri))
                {
                    IndexFormat = $"{loggingOptions.Elasticsearch.IndexName}-{environmentName.ToLower()}-{{0:yyyy.MM.dd}}",
                    AutoRegisterTemplate = true,
                    AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
                    TemplateName = loggingOptions.Elasticsearch.IndexName
                };

                if (!string.IsNullOrEmpty(loggingOptions.Elasticsearch.Username) && 
                    !string.IsNullOrEmpty(loggingOptions.Elasticsearch.Password))
                {
                    elasticOptions.ModifyConnectionSettings = conn => conn.BasicAuthentication(
                        loggingOptions.Elasticsearch.Username, 
                        loggingOptions.Elasticsearch.Password);
                }

                loggerConfig.WriteTo.Elasticsearch(elasticOptions);
            }
        }

        /// <summary>
        /// 解析日志级别
        /// </summary>
        /// <param name="logLevel">日志级别字符串</param>
        /// <returns></returns>
        private static LogEventLevel ParseLogLevel(string logLevel)
        {
            return logLevel.ToLower() switch
            {
                "verbose" => LogEventLevel.Verbose,
                "debug" => LogEventLevel.Debug,
                "information" => LogEventLevel.Information,
                "warning" => LogEventLevel.Warning,
                "error" => LogEventLevel.Error,
                "fatal" => LogEventLevel.Fatal,
                _ => LogEventLevel.Information
            };
        }
    }
}